using Microsoft.Practices.EnterpriseLibrary.Data;
using System.Data;
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;

/*

    HOW TO HOST THE WINFX SERVICE IN THIS LIBRARY IN ANOTHER PROJECT
    You will need to do the following things: 
    1)    Add a Host project to your solution
        a.    Right click on your solution
        b.    Select Add
        c.    Select New Project
        d.    Choose an appropriate Host project type (e.g. Console Application)
    2)    Add a new source file to your Host project
        a.    Right click on your Host project
        b.    Select Add
        c.    Select New Item
        d.    Select "Code File"
    3)    Paste the contents of the "MyServiceHost" class below into the new Code File
    4)    Add an "Application Configuration File" to your Host project
        a.    Right click on your Host project
        b.    Select Add
        c.    Select New Item
        d.    Select "Application Configuration File"
    5)    Paste the contents of the App.Config below that defines your service endoints into the new Config File
    6)    Add the code that will host, start and stop the service
        a.    Call MyServiceHost.StartService() to start the service and MyServiceHost.EndService() to end the service
    7)    Add a Reference to System.ServiceModel.dll
        a.    Right click on your Host Project
        b.    Select "Add Reference"
        c.    Select "System.ServiceModel.dll"
    8)    Add a Reference from your Host project to your Service Library project
        a.    Right click on your Host Project
        b.    Select "Add Reference"
        c.    Select the "Projects" tab
    9)    Set the Host project as the "StartUp" project for the solution
        a.    Right click on your Host Project
        b.    Select "Set as StartUp Project"

    ################# START MyServiceHost.cs #################

    using System;
    using System.ServiceModel;

    // A WinFX service consists of a contract (defined below), 
    // a class which implements that interface, and configuration 
    // entries that specify behaviors and endpoints associated with 
    // that implementation (see <system.serviceModel> in your application
    // configuration file).

    internal class MyServiceHost
    {
        internal static ServiceHost myServiceHost = null;

        internal static void StartService()
        {
            //Consider putting the baseAddress in the configuration system
            //and getting it here with AppSettings
            Uri baseAddress = new Uri("http://localhost:8080/service1");

            //Instantiate new ServiceHost 
            myServiceHost = new ServiceHost(typeof(ProductsService.service1), baseAddress);

            //Open myServiceHost
            myServiceHost.Open();
        }

        internal static void StopService()
        {
            //Call StopService from your shutdown logic (i.e. dispose method)
            if (myServiceHost.State != CommunicationState.Closed)
                myServiceHost.Close();
        }
    }

    ################# END MyServiceHost.cs #################
    ################# START App.config or Web.config #################

    <system.serviceModel>
    <services>
         <service name="ProductsService.service1">
           <endpoint contract="ProductsService.IService1" binding="wsHttpBinding"/>
         </service>
       </services>
    </system.serviceModel>

    ################# END App.config or Web.config #################

*/
//namespace ProductsService
//{
//    // You have created a class library to define and implement your WinFX service.
//    // You will need to add a reference to this library from another project and add 
//    // the code to that project to host the service as described below.  Another way
//    // to create and host a WinFX service is by using the Add New Item, WinFX Service 
//    // template within an existing project such as a Console Application or a Windows 
//    // Application.

//    [ServiceContract()]
//    public interface IService1
//    {
//        [OperationContract]
//        string MyOperation1(string myValue);
//        [OperationContract]
//        string MyOperation2(DataContract1 dataContractValue);
//    }

//    public class service1 : IService1
//    {
//        public string MyOperation1(string myValue)
//        {
//            return "Hello: " + myValue;
//        }
//        public string MyOperation2(DataContract1 dataContractValue)
//        {
//            return "Hello: " + dataContractValue.FirstName;
//        }
//    }

//    [DataContract]
//    public class DataContract1
//    {
//        string firstName;
//        string lastName;

//        [DataMember]
//        public string FirstName
//        {
//            get { return firstName; }
//            set { firstName = value; }
//        }
//        [DataMember]
//        public string LastName
//        {
//            get { return lastName; }
//            set { lastName = value; }
//        }
//    }
//}

namespace Products
{
    // Data contract describing the details of a product
    [DataContract]
    public class Product
    {
        [DataMember]
        public string Name;

        [DataMember]
        public string ProductNumber;

        [DataMember]
        public string Color;

        [DataMember]
        public decimal ListPrice;
    }

    // Service contract describing the operations provided by the WCF service
    [ServiceContract]
    public interface IProductsService
    {
        // Get the product number of every product
        [OperationContract]
        List<string> ListProducts();

        // Get the details of a single product
        [OperationContract]
        Product GetProduct(string productNumber);

        // Get the current stock level for a product
        [OperationContract]
        int CurrentStockLevel(string productNumber);

        // Change the stock level for a product
        [OperationContract]
        bool ChangeStockLevel(string productNumber, int newStockLevel, string shelf, int bin);
    }

    // WCF service class that implements the service contract
    public class ProductsServiceImpl : IProductsService
    {
        public List<string> ListProducts()
        {
            // Read the configuration information for connecting to the AdventureWorks database
            Database dbAdventureWorks = DatabaseFactory.CreateDatabase("AdventureWorksConnection");

            // Retrieve the details of all products by using a DataReader
            string queryString = @"SELECT ProductNumber 
                                   FROM Production.Product";
            IDataReader productsReader = dbAdventureWorks.ExecuteReader(CommandType.Text, queryString);
            
            // Create and populate a list of products
            List<string> productsList = new List<string>();
            while (productsReader.Read())
            {
                string productNumber = productsReader.GetString(0);
                productsList.Add(productNumber);
            }

            //Return the list of products
            return productsList;
        }

        public Product GetProduct(string productNumber)
        {
            // Connect to the AdventureWorks database
            Database dbAdventureWorks = DatabaseFactory.CreateDatabase("AdventureWorksConnection");

            // Retrieve the details of the selected product by using a DataReader
            string queryString = @"SELECT ProductNumber, Name, Color, ListPrice 
                                   FROM Production.Product
                                   WHERE ProductNumber = '" + productNumber + "'";
            IDataReader productsReader = dbAdventureWorks.ExecuteReader(CommandType.Text, queryString);

            // Create and populate a product
            Product product = new Product();
            if (productsReader.Read())
            {
                product.ProductNumber = productsReader.GetString(0);
                product.Name = productsReader.GetString(1);
                if (productsReader.IsDBNull(2))
                {
                    product.Color = "N/A";
                }
                else
                {
                    product.Color = productsReader.GetString(2);
                }
                product.ListPrice = productsReader.GetDecimal(3);
            }

            //Return the product
            return product;
        }

        public int CurrentStockLevel(string productNumber)
        {
            // Connect to the AdventureWorks database
            Database dbAdventureWorks = DatabaseFactory.CreateDatabase("AdventureWorksConnection");

            // Obtain the current stock level of the selected product
            // The stock level can be found by summing the quantity of the product available in all bins in the ProductInventory table
            // The ProductID value has to be retrieved from the Product table
            string queryString = @"SELECT SUM(Quantity) 
                                   FROM Production.ProductInventory 
                                   WHERE ProductID = 
                                      (SELECT ProductID 
                                       FROM Production.Product 
                                       WHERE ProductNumber = '" + productNumber + "')";
            int stockLevel = (int)dbAdventureWorks.ExecuteScalar(CommandType.Text, queryString);
            
            //Return the current stock level
            return stockLevel;
        }

        public bool ChangeStockLevel(string productNumber, int newStockLevel, string shelf, int bin)
        {
            // Connect to the AdventureWorks database
            Database dbAdventureWorks = DatabaseFactory.CreateDatabase("AdventureWorksConnection");

            // Modify the current stock level of the selected product
            // The ProductID value has to be retrieved from the Product table
            string updateString = @"UPDATE Production.ProductInventory 
                                    SET Quantity = Quantity + " + newStockLevel +
                                   "WHERE Shelf = '" + shelf + "'" +
                                   "AND Bin = " + bin +
                                  @"AND ProductID = 
                                        (SELECT ProductID 
                                         FROM Production.Product 
                                         WHERE ProductNumber = '" + productNumber + "')";
            int numRowsChanged = (int)dbAdventureWorks.ExecuteNonQuery(CommandType.Text, updateString);

            // If no rows were updated, return false to indicate that the input parameters 
            // did not identify a valid product and location.
            // Otherwise return true to indicate success
            return (numRowsChanged != 0);
        }
    }
}
